package com.joysuch.wwyt.workflow.service.impl;


import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.json.JSONUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import cn.zlg.common.util.CollectionUtils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.base.Strings;
import com.google.gson.reflect.TypeToken;
import com.joysuch.wwyt.api.sync.SyncConstants;
import com.joysuch.wwyt.common.constant.Constant;
import com.joysuch.wwyt.config.RedisClient;
import com.joysuch.wwyt.core.common.ajax.ResultBean;
import com.joysuch.wwyt.core.common.util.MapUtils;
import com.joysuch.wwyt.core.entity.BaseConfig;
import com.joysuch.wwyt.core.enums.BaseConfigCodes;
import com.joysuch.wwyt.core.enums.DeleteFlag;
import com.joysuch.wwyt.core.enums.Status;
import com.joysuch.wwyt.core.exception.ApiBusinessException;
import com.joysuch.wwyt.core.service.BaseConfigService;
import com.joysuch.wwyt.core.service.BaseRoleDataAuthService;
import com.joysuch.wwyt.core.support.Context;
import com.joysuch.wwyt.util.GsonUtils;
import com.joysuch.wwyt.workflow.WorkFlowConstants;
import com.joysuch.wwyt.workflow.bean.*;
import com.joysuch.wwyt.workflow.entity.*;
import com.joysuch.wwyt.workflow.entity.bo.ComponentStatisticBO;
import com.joysuch.wwyt.workflow.entity.dto.*;
import com.joysuch.wwyt.workflow.entity.vo.SummaryInquiryTaskVO;
import com.joysuch.wwyt.workflow.entity.vo.WorkFlowProcessStatVO;
import com.joysuch.wwyt.workflow.entity.vo.WorkflowTaskSimpleVO;
import com.joysuch.wwyt.workflow.enums.ComponentCountEnum;
import com.joysuch.wwyt.workflow.enums.FormDataStatisticSyncEnum;
import com.joysuch.wwyt.workflow.enums.WorkFlowExportComponentEnum;
import com.joysuch.wwyt.workflow.enums.WorkFlowProcessEnum;
import com.joysuch.wwyt.workflow.mapper.WorkflowTaskMapper;
import com.joysuch.wwyt.workflow.repository.WorkFlowMenuDao;
import com.joysuch.wwyt.workflow.repository.WorkFlowProcessDao;
import com.joysuch.wwyt.workflow.service.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Slf4j
@Service
public class WorkFlowTaskProcessListServiceImpl implements WorkFlowTaskProcessListService {

    @Resource
    private BaseConfigService baseConfigService;
    @Autowired
    private WorkflowTaskMapper workflowTaskMapper;
    @Autowired
    private WorkFlowMenuDao workFlowMenuDao;
    @Autowired
    private BaseSummaryInquiryConfigService summaryInquiryConfigService;
    @Autowired
    private BaseRoleDataAuthService baseRoleDataAuthService;
    @Autowired
    private WorkflowTaskExportTemplateService workflowTaskExportTemplateService;
    @Autowired
    private WorkFlowProcessDao workFlowProcessDao;
    @Autowired
    private WorkflowStatisticDataService workflowStatisticDataService;
    @Autowired
    private RedisClient redisClient;
    @Autowired
    private WorkflowTaskAssignService workflowTaskAssignService;

    @Autowired
    private BaseConfigService configService;

    @Override
    public ResultBean findProcessList(WorkFlowProcessTaskQueryBean queryBean) {
        Long userId = Context.getCurrentUser().getRealUserId();
        String companyCode = Context.getCompanyCode();
        Long tenantId = Context.getCurrentTenantId();
        Long departId = Context.getCurrentDepartId();
        Long jobId = Context.getJobId();
        List<Long> roleIds = Context.getRoleIds();
        List<Long> processIds = queryBean.getProcessIds();
        Long typeId = queryBean.getTypeId();
        //分页获取数据
        Page<WorkflowTaskDto> page = new Page<>(queryBean.getPage(), queryBean.getSize());
        //不进行count查询
        page.setSearchCount(false);
        //构建搜索语句
        String searchSql = buildSearchSql(queryBean.getSearchJson());

        IPage<WorkflowTaskDto> taskListIPage = new Page<>();
        WorkFlowProcessEnum process = WorkFlowProcessEnum.find(queryBean.getWorkFlowProcess());
        long taskCount = 0L;

        switch (process) {
            case TO_DO_TASK:
                Set<Long> departIds = baseRoleDataAuthService.findAuthDepartIdsByCurUser();
                taskListIPage = workflowTaskMapper.queryToDoTaskPaging(page, userId, roleIds, departId, jobId, processIds, typeId, companyCode, tenantId, departIds, searchSql);
                taskCount = workflowTaskMapper.queryToDoTaskCount(userId, roleIds, departId, jobId, processIds, typeId, companyCode, tenantId, departIds, searchSql);
                break;
            case PROCESSED:
                taskListIPage = workflowTaskMapper.queryProcessedTaskPaging(page, userId, processIds, typeId, searchSql);
                taskCount = workflowTaskMapper.queryProcessedTaskCount(userId, processIds, typeId, searchSql);
                break;
            case PROPOSER:
                taskListIPage = workflowTaskMapper.queryProposerTaskPaging(page, userId, processIds, typeId, searchSql);
                taskCount = workflowTaskMapper.queryProposerTaskCount(userId, processIds, typeId, searchSql);
                break;
            case COMPLETED:
                taskListIPage = workflowTaskMapper.queryCompletedTaskPaging(page, userId, processIds, typeId, searchSql);
                taskCount = workflowTaskMapper.queryCompletedTaskCount(userId, processIds, typeId, searchSql);
                break;
            case DRAFT:
                taskListIPage = workflowTaskMapper.queryDraftTaskPaging(page, userId, processIds, typeId, searchSql);
                taskCount = workflowTaskMapper.queryDraftTaskCount(userId, processIds, typeId, searchSql);
                break;
        }
        //添加菜单按钮
        if (taskCount > 0) {
            List<WorkflowTaskDto> records = taskListIPage.getRecords();
            processIds = CollectionUtils.isEmpty(processIds) ? records.stream().map(WorkflowTaskDto::getProcessId).distinct().collect(Collectors.toList()) : processIds;
            if (CollectionUtils.isNotEmpty(processIds)) {
                Map<Long, String> workFlowMenu = buildWorkFlowMenuMap(processIds, queryBean.getWorkFlowProcess());
                records.forEach(each -> {
                    if (workFlowMenu.containsKey(each.getProcessId())) {
                        each.setOperationTypes(workFlowMenu.getOrDefault(each.getProcessId(), ""));
                    }
                });
            }
        }
        return ResultBean.pageData(taskListIPage.getRecords(), taskCount);
    }


    @Override
    public ResultBean findAllTaskList(WorkFlowProcessTaskQueryBean queryBean) {

        String companyCode = Context.getCompanyCode();
        Long tenantId = Context.getCurrentTenantId();
        List<Long> processIds = queryBean.getProcessIds();
        Long typeId = queryBean.getTypeId();
        //分页获取数据
        Page<WorkflowTaskDto> page = new Page<>(queryBean.getPage(), queryBean.getSize());
        //不进行count查询
        page.setSearchCount(false);
        //构建搜索语句
        String searchSql = buildSearchSql(queryBean.getSearchJson());

        Set<Long> departIds = baseRoleDataAuthService.findAuthDepartIdsByCurUser();
        String orderBySql = _getOrderBySql(processIds);
        IPage<WorkflowTaskDto> allTaskIPage = workflowTaskMapper.queryAllTaskPaging(page, processIds, typeId, searchSql, departIds, companyCode, tenantId, orderBySql);
        long taskCount = workflowTaskMapper.queryAllTaskCount(processIds, typeId, searchSql, departIds, companyCode, tenantId);
        //添加菜单按钮
        if (taskCount > 0) {
            processIds = CollectionUtils.isEmpty(processIds) ? allTaskIPage.getRecords().stream().map(WorkflowTaskDto::getProcessId).distinct().collect(Collectors.toList()) : processIds;
            if (CollectionUtils.isNotEmpty(processIds)) {
                Map<Long, String> workFlowMenu = buildWorkFlowMenuMap(processIds, queryBean.getWorkFlowProcess());
                allTaskIPage.getRecords().forEach(each -> {
                    if (workFlowMenu.containsKey(each.getProcessId())) {
                        each.setOperationTypes(workFlowMenu.getOrDefault(each.getProcessId(), ""));
                    }
                });
            }
        }
        return ResultBean.pageData(allTaskIPage.getRecords(), taskCount);
    }
    @Override
    public ResultBean findAllTaskNewList(WorkFlowProcessTaskQueryBean queryBean) {

        String companyCode = Context.getCompanyCode();
        Long tenantId = Context.getCurrentTenantId();
        List<Long> processIds = queryBean.getProcessIds();
        Long typeId = queryBean.getTypeId();
        //分页获取数据
        Page<WorkflowTaskDto> page = new Page<>(queryBean.getPage(), queryBean.getSize());
        //不进行count查询
        page.setSearchCount(false);
        //构建搜索语句
        String searchSql = buildSearchSql(queryBean.getSearchJson());
        Set<Long> departIds=new HashSet<>();
        if (queryBean.getDepartId()!=null) {
            departIds.add(queryBean.getDepartId());
        }

        String orderBySql = _getOrderBySql(processIds);
        Map<String,String> map = getConfig();
        IPage<WorkflowTaskDto> allTaskIPage = workflowTaskMapper.queryNewAllTaskPaging(page, processIds, typeId, searchSql, departIds,
                companyCode, tenantId, orderBySql,queryBean.getDepartName(),map.get("departName"),map.get("reportTime"),queryBean.getStartTime(),queryBean.getEndTime());
        long taskCount = workflowTaskMapper.queryAllNewTaskCount(processIds, typeId, searchSql, departIds, companyCode, tenantId,queryBean.getDepartName(),
                map.get("departName"),map.get("reportTime"),queryBean.getStartTime(),queryBean.getEndTime());
        //添加菜单按钮
        if (taskCount > 0) {
            processIds = CollectionUtils.isEmpty(processIds) ? allTaskIPage.getRecords().stream().map(WorkflowTaskDto::getProcessId).distinct().collect(Collectors.toList()) : processIds;
            if (CollectionUtils.isNotEmpty(processIds)) {
                Map<Long, String> workFlowMenu = buildWorkFlowMenuMap(processIds, queryBean.getWorkFlowProcess());
                allTaskIPage.getRecords().forEach(each -> {
                    if (workFlowMenu.containsKey(each.getProcessId())) {
                        each.setOperationTypes(workFlowMenu.getOrDefault(each.getProcessId(), ""));
                    }
                });
            }
        }
        return ResultBean.pageData(allTaskIPage.getRecords(), taskCount);
    }



    public Map getConfig(){
        Map<String,String> map=new HashMap();
        map.put("bizName","隐患管理");

        map.put("reportTime","上报时间");
        map.put("departName","责任单位");
        map.put("dateName","要求完成日期");
        map.put("verificationName","验证日期");
        BaseConfig config = configService.findConfig(BaseConfigCodes.WORKFLOW_FIELD);
        BaseConfig processConfig = configService.findConfig(BaseConfigCodes.WORKFLOW_PROCESS_NAME);
        if (config!=null&&StringUtils.isNotBlank(config.getValue())){
            String[] split = config.getValue().split(",");
            if (split[0]!=null) {
                map.put("reportTime",split[0]);
            }
            if (split[1]!=null) {
                map.put("departName",split[1]);
            }

            if (split[2]!=null) {
                map.put("dateName",split[2]);
            }
            if (split[3]!=null) {
                map.put("verificationName",split[3]);
            }
        }
        if (processConfig!=null&&StringUtils.isNotBlank(processConfig.getValue())){
            String[] split = processConfig.getValue().split(",");
            if (split[0]!=null) {
                map.put("bizName",split[0]);
            }
        }
        return map;
    }

    private String _getOrderBySql(List<Long> processIds) {
        if (processIds == null || processIds.size() != 1) {
            return null;
        }
        Long pid = processIds.get(0);
        WorkFlowProcess process = workFlowProcessDao.findById(pid).get();
        String bizName = process.getBizName();
        String configKey = bizName + ".排序";
        BaseConfig beforeConfig = baseConfigService.findConfig(Context.getCurrentTenantId(), Context.getCompanyCode(), configKey);
        if (beforeConfig == null || Strings.isNullOrEmpty(beforeConfig.getValue())) {
            return null;
        }

        try {
            StringBuilder builder = new StringBuilder();
            builder.append(" order by ");
            String config = beforeConfig.getValue().trim();
            String[] fields = config.split(",");
            JSONObject srcLabelKey = JSONObject.parseObject(process.getFormKeyLabelJson());
            for (int i=0; i<fields.length; i++) {
                if (i > 0) {
                    builder.append(" , ");
                }
                String field = fields[i];
                String[] labelOrderDefault = field.split("\\|");
                String fieldExp = "JSON_EXTRACT(form_data, '$." + srcLabelKey.get(labelOrderDefault[0]) + "')";
                builder.append("if(ISNULL(" + fieldExp + "), '" + labelOrderDefault[2] + "', " + fieldExp + ")")
                        .append("升序".equals(labelOrderDefault[1]) ? " ASC " : " DESC ");
            }
            return builder.toString();
        } catch (Exception e) {
            return null;
        }
    }

    @Override
    public ResultBean findTaskSimpleList(WorkFlowProcessTaskQueryBean queryBean) throws ApiBusinessException {
        String companyCode = Context.getCompanyCode();
        Long tenantId = Context.getCurrentTenantId();
        Long typeId = queryBean.getTypeId();
        Integer finishFlag = queryBean.getFinishFlag();
        //分页获取数据
        Page<WorkflowTaskDto> page = new Page<>(queryBean.getPage() + 1, queryBean.getSize());
        //不进行count查询
        page.setSearchCount(false);
        //构建搜索语句
        List<WorkFlowProcess> workFlowProcesses = workFlowProcessDao.findByBizTypeName(queryBean.getBizTypeName());
        if (CollectionUtils.isEmpty(workFlowProcesses)) {
            throw new ApiBusinessException(Status.PROCESS_BIZ_TYPE_NAME_NOT_FIND);
        }
        List<Long> processIds = workFlowProcesses.stream().map(WorkFlowProcess::getId).collect(Collectors.toList());
        String searchSql = buildSearchSql(queryBean.getSearchJson());
        IPage<WorkflowTaskDto> allTaskIPage = workflowTaskMapper.queryTaskSimpleListPaging(page, processIds, typeId, searchSql, companyCode, tenantId, finishFlag);
        long taskCount = workflowTaskMapper.queryTaskSimpleListCount(processIds, typeId, searchSql, companyCode, tenantId, finishFlag);
        List<WorkflowTaskDto> allTask = allTaskIPage.getRecords();
        List<WorkFlowProcess> processList = workFlowProcessDao.findAllById(processIds);
        Map<Long, WorkFlowProcess> processMap = processList.stream().collect(Collectors.toMap(WorkFlowProcess::getId, Function.identity()));
        List<WorkflowTaskSimpleVO> taskSimpleList = new ArrayList<>();
        allTask.forEach(each -> {
            WorkflowTaskSimpleVO taskSimpleVO = new WorkflowTaskSimpleVO();
            taskSimpleVO.setId(each.getId());
            taskSimpleVO.setBizName(each.getBizName());
            WorkFlowProcess workFlowProcess = processMap.get(each.getProcessId());
            String addressKey = findProcessFormJsonKey("作业地点", workFlowProcess.getFormJson());
            if (StringUtils.isNotBlank(addressKey)) {
                String address = findTaskFromDataValByKey(addressKey, each.getFormData());
                taskSimpleVO.setTaskAddress(address);
            }
            String contentKey = findProcessFormJsonKey("作业内容", workFlowProcess.getFormJson());
            if (StringUtils.isNotBlank(contentKey)) {
                String content = findTaskFromDataValByKey(contentKey, each.getFormData());
                taskSimpleVO.setTaskContent(content);
            }
            taskSimpleVO.setTaskCode(each.getTaskCode());
            taskSimpleList.add(taskSimpleVO);
        });
        return ResultBean.pageData(taskSimpleList, taskCount);
    }

    @Override
    public String findTaskFromDataValByKey(String key, String fromData) {
        if (!StringUtils.isEmpty(fromData)) {
            JSONObject fromJson = JSON.parseObject(fromData);
            Object obj = fromJson.get(key);
            if (obj != null) {
                if (!JSONUtil.isJsonObj(obj.toString()) && !JSONUtil.isJsonArray(obj.toString())) {
                    return (String) fromJson.get(key);
                }
            }
        }

        return null;
    }

    @Override
    public MapUtils.SafetyPoints findTaskFromDataoOcationByKey(String key, String fromData) {
        if (!StringUtils.isEmpty(fromData) && StringUtils.isNotBlank(key)) {
            JSONObject fromJson = JSON.parseObject(fromData);
            Object obj = fromJson.get(key);
            if (obj != null) {
                return MapUtils.parse3DSafetyPointsFromJson((String) fromJson.get(key));
            }
        }
        return null;
    }

    @Override
    public WorkFlowProcessStatVO findProcessStat(WorkFlowProcessTaskQueryBean queryBean) {
        Long userId = Context.getCurrentUser().getRealUserId();
        Long departId = Context.getCurrentDepartId();
        Long jobId = Context.getJobId();
        List<Long> roleIds = Context.getRoleIds();
        List<Long> processIds = queryBean.getProcessIds();
        Long typeId = queryBean.getTypeId();
        String companyCode = Context.getCompanyCode();
        Long tenantId = Context.getCurrentTenantId();
        Set<Long> departIds = baseRoleDataAuthService.findAuthDepartIdsByCurUser();
        long todoTaskCount = workflowTaskMapper.queryToDoTaskCount(userId, roleIds, departId, jobId, processIds, typeId, companyCode, tenantId, departIds, null);
        long processedTaskCount = workflowTaskMapper.queryProcessedTaskCount(userId, processIds, typeId, null);
        long proposerTaskCount = workflowTaskMapper.queryProposerTaskCount(userId, processIds, typeId, null);
        long completedTaskCount = workflowTaskMapper.queryCompletedTaskCount(userId, processIds, typeId, null);
        long draftCount = workflowTaskMapper.queryDraftTaskCount(userId, processIds, typeId, null);
        long allTaskCount = workflowTaskMapper.queryAllTaskCount(processIds, typeId, null, departIds, companyCode, tenantId);
        return WorkFlowProcessStatVO.builder().toDoTaskCount(todoTaskCount).processedCount(processedTaskCount).
                proposerCount(proposerTaskCount).completedCount(completedTaskCount).allCount(allTaskCount).draftCount(draftCount).build();
    }

    @Override
    public Map<Long, String> buildWorkFlowMenuMap(List<Long> processIds, String workFlowProcess) {
        List<WorkFlowMenu> menuList = workFlowMenuDao.findOptButtonsByWorkFlowId(processIds, WorkFlowProcessEnum.ALL.getValue().equals(workFlowProcess) ? 1 : 0);
        return menuList.stream().collect(Collectors.toMap(WorkFlowMenu::getWorkFlowId, WorkFlowMenu::getOptButtons, (v1, v2) -> v2));
    }

    @Override
    public ResultBean findSummaryInquiryTaskList(SummaryInquiryQueryBean queryBean) {

        List<SummaryReportConfigBean> reportConfigs = queryBean.getReportConfigs();

        //分页获取数据
        Page<SummaryInquiryTaskDto> page = new Page<>(queryBean.getPage() + 1, queryBean.getSize());
        //不进行count查询
        page.setSearchCount(false);
        //构建搜索语句
        SumInquiryConditionTuple tuple = this.buildSumInquiryCondition(reportConfigs);

        IPage<SummaryInquiryTaskDto> taskListIPage = new Page<>();
        long taskCount = 0L;
        if (tuple != null) {
            Set<Long> departIds = baseRoleDataAuthService.findAuthDepartIdsByCurUser();
            //sql中拼接了process id
            if (StringUtils.isNotEmpty(tuple.getSearchSql())) {
                tuple.getProcessIds().clear();
            }
            String orgCode = Context.getCurrentOrgCode();
            Long tenentId = Context.getCurrentTenantId();
            taskListIPage = workflowTaskMapper.querySummaryInquiryTaskPaging(page, tuple.getProcessIds(), tuple.getSearchSql(), departIds, orgCode, tenentId);
            taskCount = workflowTaskMapper.querySummaryInquiryTaskCount(tuple.getProcessIds(), tuple.getSearchSql(), departIds, orgCode, tenentId);
        }
        //获取process字段映射
        Map<Long, List<String>> processFieldMap = this.getProcessFieldMap(queryBean.getSummaryInquiryConfigId());
        return ResultBean.success(SummaryInquiryTaskVO.builder().taskList(taskListIPage.getRecords()).processField(processFieldMap).count(taskCount).build());
    }

    private Map<Long, List<String>> getProcessFieldMap(Long configId) {
        WorkFlowInquiryConfigBean workFlowInquiryConfig = summaryInquiryConfigService.findById(configId);
        List<WorkFlowReportConfigDto> reportConfigList = workFlowInquiryConfig.getReportConfigs();
        List<Long> processIds = workFlowInquiryConfig.getProcessIds();
        Map<Long, List<String>> processFieldMap = new HashMap<>();

        if (CollectionUtils.isNotEmpty(reportConfigList)) {
            reportConfigList.forEach(each -> {
                List<WorkFlowReportFieldDto> reportFields = each.getReportFields();
                //汇总字段列表
                reportFields.forEach(field -> {
                    List<String> fields;
                    if (processFieldMap.get(field.getProcessId()) == null) {
                        fields = new ArrayList<>();
                    } else {
                        fields = processFieldMap.get(field.getProcessId());
                    }
                    fields.add(field.getKey());
                    processFieldMap.put(field.getProcessId(), fields);
                });
            });
            //默认添加 code 异常状态、流程状态
            processIds.forEach(id -> {
                List<String> fields;
                if (processFieldMap.containsKey(id)) {
                    fields = processFieldMap.get(id);
                    fields.add(0, WorkFlowConstants.WORK_FLOW_TASK_ID_KEY);
                    fields.add(WorkFlowConstants.WORK_FLOW_TASK_STATUS_KEY);
                    fields.add(WorkFlowConstants.WORK_FLOW_EXCEPTION_STATUS);
                } else {
                    fields = new ArrayList<>();
                    fields.add(0, WorkFlowConstants.WORK_FLOW_TASK_ID_KEY);
                    fields.add(WorkFlowConstants.WORK_FLOW_TASK_STATUS_KEY);
                    fields.add(WorkFlowConstants.WORK_FLOW_EXCEPTION_STATUS);
                    processFieldMap.put(id, fields);
                }
            });
        }
        return processFieldMap;
    }

    /**
     * 构建search查询条件 和老版本一致后续优化
     *
     * @param reportConfigs
     * @return
     */
    private SumInquiryConditionTuple buildSumInquiryCondition(List<SummaryReportConfigBean> reportConfigs) {

        StringBuilder searchSql = new StringBuilder();
        List<Long> processIds = new ArrayList<>();
        for (SummaryReportConfigBean config : reportConfigs) {

            List<WorkFlowTaskSearchFieldBean> fieldList = config.getFieldList();
            processIds.add(config.getProcessId());

            if (CollectionUtils.isNotEmpty(fieldList)) {
                if (searchSql.length() == 0) {
                    searchSql.append(" and ( case ");
                }
                searchSql.append(" when  wt.process_id = ").append(config.getProcessId()).append(" then ( ");
                for (int i = 0; i < fieldList.size(); i++) {
                    WorkFlowTaskSearchFieldBean each = fieldList.get(i);
                    if ("between".equalsIgnoreCase(each.getOperation())) {
                        //两个字段间查询
                        if ("date".equalsIgnoreCase(each.getFormat())) {
                            Date minDate = DateUtil.parse(each.getMinValue().toString());
                            Date maxDate = DateUtil.endOfDay(DateUtil.parse(each.getMaxValue().toString()));
                            searchSql.append(" str_to_date(replace(JSON_EXTRACT(form_data, '$.").append("\"").append(each.getKey()).append("\"").append("'),'\\\"',''),'%Y-%m-%d %H:%i:%s')");
                            searchSql.append(" between ").append("'").append(minDate).append("'").append(" and ").append("'").append(maxDate).append("'");
                        }
                    }
                    if (StringUtils.isNotEmpty(each.getValue())) {
                        searchSql.append(" JSON_EXTRACT(form_data, '$.").append("\"").append(each.getKey()).append("\"").append("')").append(" like '%").append(each.getValue()).append("%' ");
                    }
                    if (i == fieldList.size() - 1) {
                        searchSql.append(" ) ");
                    } else {
                        searchSql.append(" and ");
                    }

                }
            }
        }
        return SumInquiryConditionTuple.builder().searchSql(searchSql.length() > 0 ? searchSql.append(" END )").toString() : null).processIds(processIds).build();
    }


    @Override
    public String findProcessFormJsonKey(String label, String fromJsonStr) {
        if (StringUtils.isNotBlank(fromJsonStr)) {
            JSONObject fromJson = JSON.parseObject(fromJsonStr);
            Object temArrObj = fromJson.get(WorkFlowConstants.FROM_JSON_KEY_TEMARR);
            if (temArrObj != null) {
                JSONArray temArr = JSONObject.parseArray(temArrObj.toString());
                for (Object tem : temArr) {
                    Map<String, Object> tableMap = JSONObject.parseObject(JSON.toJSONString(tem));
                    Object tableArrObj = tableMap.get(WorkFlowConstants.FROM_JSON_KEY_TABLEARR);
                    JSONArray tableArr = JSONObject.parseArray(tableArrObj.toString());
                    for (Object table : tableArr) {
                        Map<String, Object> beanMap = JSONObject.parseObject(JSON.toJSONString(table));
                        Object labelObj = beanMap.get(WorkFlowConstants.FROM_JSON_KEY_LABEL);
                        if (labelObj != null) {
                            String value = (String) labelObj;
                            if (label.equals(value)) {
                                Object valueObj = beanMap.get(WorkFlowConstants.FROM_JSON_KEY_SELF);
                                if (valueObj != null) {
                                    return (String) valueObj;
                                }
                            }
                        }
                    }
                }
            }
        }
        return null;
    }

    @Override
    public String findProcessMapFormJsonKey(String fromJsonStr) {
        if (StringUtils.isNotBlank(fromJsonStr)) {
            JSONObject fromJson = JSON.parseObject(fromJsonStr);
            Object temArrObj = fromJson.get(WorkFlowConstants.FROM_JSON_KEY_TEMARR);
            if (temArrObj != null) {
                JSONArray temArr = JSONObject.parseArray(temArrObj.toString());
                for (Object tem : temArr) {
                    Map<String, Object> tableMap = JSONObject.parseObject(JSON.toJSONString(tem));
                    Object tableArrObj = tableMap.get(WorkFlowConstants.FROM_JSON_KEY_TABLEARR);
                    JSONArray tableArr = JSONObject.parseArray(tableArrObj.toString());
                    for (Object table : tableArr) {
                        Map<String, Object> beanMap = JSONObject.parseObject(JSON.toJSONString(table));
                        Object labelObj = beanMap.get(WorkFlowConstants.FROM_JSON_KEY_DIA_TYPE);
                        if (labelObj != null) {
                            String value = (String) labelObj;
                            if ("mapPlace".equals(value)) {
                                Object valueObj = beanMap.get(WorkFlowConstants.FROM_JSON_KEY_SELF);
                                if (valueObj != null) {
                                    return (String) valueObj;
                                }
                            }
                        }
                    }
                }
            }
        }
        return null;
    }

    @Override
    public Long checkExportData(WorkFlowProcessTaskQueryBean queryBean) throws ApiBusinessException {
        WorkFlowProcessEnum process = WorkFlowProcessEnum.find(queryBean.getWorkFlowProcess());
        ResultBean bean;
        if (process == WorkFlowProcessEnum.ALL) {
            bean = findAllTaskList(queryBean);
        } else {
            bean = findProcessList(queryBean);
        }
        if (bean.getLength() == 0) {
            throw new ApiBusinessException(Status.EXPORT_PROCESS_DATA_IS_EMPTY);
        }
        return bean.getLength();
    }

    @Override
    public void export(WorkFlowProcessTaskQueryBean queryBean, HttpServletResponse response) throws IOException {
        WorkFlowProcessEnum process = WorkFlowProcessEnum.find(queryBean.getWorkFlowProcess());
        ResultBean bean;
        if (process == WorkFlowProcessEnum.ALL) {
            bean = findAllTaskList(queryBean);
        } else {
            bean = findProcessList(queryBean);
        }
        List<WorkflowTaskDto> taskList = (List<WorkflowTaskDto>) bean.getData();
        WorkflowTaskExportTemplate template = workflowTaskExportTemplateService.getById(queryBean.getTemplateId());
        String field = template.getFieldList();
        List<WorkFlowExportJsonKey> headerValList = GsonUtils.fromJson(field, new TypeToken<List<WorkFlowExportJsonKey>>() {
        }.getType());
        List<Map<String, Object>> exportDataList = new ArrayList<>();
        for (WorkflowTaskDto task : taskList) {
            Map<String, Object> dataMap = new LinkedHashMap<>();
            for (WorkFlowExportJsonKey each : headerValList) {
                String parseValue = Constant.EMPTY_STR;
                //如果数据是复选框
                if (WorkFlowExportComponentEnum.CHECKBOX.getValue().equals(each.getType())) {
                    parseValue = parseCheckboxValue(task.getFormData(), each.getKey());
                } else {
                    //数据存在单选多选,单选和多选数据结构不一致
                    String formDataStr = task.getFormData();
                    JSONObject fromData = JSON.parseObject(formDataStr);
                    Object value = fromData.get(each.getKey());
                    if (value != null) {
                        if (JSONUtil.isJsonArray(value.toString())) {
                            parseValue = parseMultipleChoice(value);
                        } else {
                            parseValue = fromData.get(each.getKey()) != null ? fromData.get(each.getKey()).toString() : parseValue;
                        }
                    }
                }
                dataMap.put(each.getKey(), parseValue);
            }
            exportDataList.add(dataMap);
        }
        OutputStream out = response.getOutputStream();
        ExcelWriter writer = ExcelUtil.getWriter(true);
        headerValList.forEach(each -> writer.addHeaderAlias(each.getKey(), each.getLabel()));
        writer.write(exportDataList, true);
        String fileName = StringUtils.isNotBlank(queryBean.getFileName()) ? queryBean.getFileName() : "processData";
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
        response.setHeader("Content-Disposition", String.format("attachment;filename=%s", fileName + ".xlsx"));
        writer.flush(out, true);
        writer.close();
        IoUtil.close(out);
    }

    private String parseMultipleChoice(Object value) {
        JSONArray multipleChoice = JSON.parseArray(value.toString());
        StringBuilder stringBuilder = new StringBuilder();
        for (Object json : multipleChoice) {
            if (JSONUtil.isJsonObj(json.toString())) {
                JSONObject choiceJson = JSON.parseObject(json.toString());
                if (stringBuilder.length() > 0) {
                    stringBuilder.append(Constant.SPLIT_COMMA);
                }
                stringBuilder.append(choiceJson.get("label"));
            }
        }
        return stringBuilder.length() > 0 ? stringBuilder.toString() : Constant.EMPTY_STR;
    }

    private String parseCheckboxValue(String formDataStr, String key) {
        JSONObject formData = JSON.parseObject(formDataStr);
        Object checkBoxObj = formData.get(key);
        String checkBoxValue = Constant.EMPTY_STR;
        StringBuilder stringBuilder = new StringBuilder();
        if (checkBoxObj != null) {
            if (JSONUtil.isJsonArray(checkBoxObj.toString())) {
                JSONArray checkBoxArr = JSON.parseArray(checkBoxObj.toString());
                if (checkBoxArr.size() > 0) {
                    for (Object json : checkBoxArr) {
                        if (stringBuilder.length() > 0) {
                            stringBuilder.append(Constant.SPLIT_COMMA);
                        }
                        stringBuilder.append(json);
                    }
                }
                return stringBuilder.toString();
            }
        }
        return checkBoxValue;
    }


    @Async("formDataStatisticAsync")
    @Override
    public void asyncFormDataToStatisticData(Long processId, Long tenantId, String companyCode) {
        int count = workflowTaskMapper.countByProcessIdAndSyncStatus(processId, FormDataStatisticSyncEnum.NOT_SYNC.getValue());
        int batchSize = 500;
        int num = (count + batchSize - 1) / batchSize;
        for (int i = 0; i < num; i++) {
            List<WorkflowStatisticDto> statisticList = workflowTaskMapper.queryFormDataByProcessIdAndSyncStatus(processId, FormDataStatisticSyncEnum.NOT_SYNC.getValue(), batchSize);
            statisticList.forEach(each -> saveStatisticData(each.getId(), processId, each.getCode(), each.getFormData(), each.getAuthType(), each.getAuthValue(), tenantId, companyCode));
        }
    }

    @Override
    @Async("formDataStatisticAsync")
    public void saveStatisticData(final Long id, final Long processId, final String taskCode, final String formData,
                                  final String authType, final String authValue, final Long tenantId, final String companyCode) {
        try {
            //批量删除统计数据，会存在组件多选的情况，多选数据没有唯一标识，没办法更新
            deleteTaskStatisticList(processId, taskCode);
            JSONObject formJson = JSONObject.parseObject(formData);
            String redisKey = String.format(BaseConfigCodes.COMPONENT_COUNT_KEY, tenantId, companyCode);
            List<ComponentStatisticBO> statisticList = redisClient.hget(redisKey, processId);
            List<WorkflowStatisticData> dataList = new ArrayList<>();
            for (int i = 0; i < statisticList.size(); i++) {
                ComponentStatisticBO each = JSONObject.parseObject(JSON.toJSONString(statisticList.get(i)), ComponentStatisticBO.class);
                Object value = formJson.get(each.getKey());
                if (value != null) {
                    if ((WorkFlowConstants.KEY_DIA_TYPE_DEPARTMENT.equals(each.getComponentDiaType()) ||
                            WorkFlowConstants.KEY_DIA_TYPE_PERSON.equals(each.getComponentDiaType())) && JSONUtil.isJsonArray(value.toString())) {
                        JSONArray jsonArray = JSON.parseArray((value.toString()));
                        for (Object json : jsonArray) {
                            if (JSONUtil.isJsonObj(json.toString())) {
                                JSONObject jsonObject = JSON.parseObject(json.toString());
                                Object fromValue = jsonObject.get(WorkFlowConstants.FROM_JSON_KEY_LABEL);
                                addDataList(processId, taskCode, dataList, each, fromValue, tenantId, companyCode);
                            }
                        }
                    } else {
                        addDataList(processId, taskCode, dataList, each, value, tenantId, companyCode);
                    }
                }
            }
            if (CollectionUtils.isNotEmpty(dataList)) {
                workflowStatisticDataService.saveOrUpdateBatch(dataList);
            }
            saveWorkflowTaskAssign(id, taskCode, authType, authValue, tenantId, companyCode);
            updateSyncStatus(id, true);
        } catch (Throwable e) {
            log.error("Save Statistic data error,task code {} ", taskCode, e);
        }
    }

    private void addDataList(Long processId, String taskCode, List<WorkflowStatisticData> dataList, ComponentStatisticBO statisticBO, Object value, Long tenantId, String companyCode) {
        WorkflowStatisticData statisticData = new WorkflowStatisticData();
        statisticData.setProcessId(processId);
        statisticData.setComponentKey(statisticBO.getKey());
        statisticData.setFieldName(statisticBO.getLabel());
        final String componentType = statisticBO.getComponentType();
        statisticData.setComponentType(componentType);
        statisticData.setTaskCode(taskCode);
        if (ComponentCountEnum.TIME.getCode().equals(componentType)) {
            DateTime time = DateUtil.parse(value.toString());
            statisticData.setFieldValue(DateUtil.format(time, DatePattern.NORM_DATETIME_PATTERN));
        } else {
            statisticData.setFieldValue(value.toString());
        }
        statisticData.setTenentId(tenantId);
        statisticData.setOrgCode(companyCode);
        dataList.add(statisticData);
    }

    private void saveWorkflowTaskAssign(Long taskRecordId, String taskCode, String authType, String authValue, Long tenantId, String companyCode) {
        WorkflowTaskAssign assign = new WorkflowTaskAssign();
        assign.setTaskRecordId(taskRecordId);
        assign.setAuthType(authType);
        assign.setTaskCode(taskCode);
        assign.setTenentId(tenantId);
        assign.setOrgCode(companyCode);
        if (StringUtils.isNotBlank(authValue) && authValue.contains(Constant.SPLIT_COMMA)) {
            List<String> list = Arrays.asList(authValue.split(Constant.SPLIT_COMMA));
            list.forEach(each -> {
                assign.setAuthValue(each);
                workflowTaskAssignService.save(assign);
            });
        } else {
            assign.setAuthValue(authValue);
            workflowTaskAssignService.save(assign);
        }
    }

    @Override
    public int updateSyncStatus(Long id, boolean status) {
        int syncStatus = status ? FormDataStatisticSyncEnum.SYNC_SUCCESS.getValue() : FormDataStatisticSyncEnum.SYNC_FAILED.getValue();
        return workflowTaskMapper.updateSyncStatus(id, syncStatus);
    }

    private List<WorkflowStatisticData> findTaskStatisticList(Long processId, String taskCode) {
        LambdaQueryWrapper<WorkflowStatisticData> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(WorkflowStatisticData::getDeleteFlag, DeleteFlag.EFFECTIVE.getCode());
        wrapper.eq(WorkflowStatisticData::getProcessId, processId);
        wrapper.eq(WorkflowStatisticData::getTaskCode, taskCode);
        return workflowStatisticDataService.list(wrapper);
    }

    private boolean deleteTaskStatisticList(Long processId, String taskCode) {
        LambdaQueryWrapper<WorkflowStatisticData> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(WorkflowStatisticData::getProcessId, processId);
        wrapper.eq(WorkflowStatisticData::getTaskCode, taskCode);
        return workflowStatisticDataService.remove(wrapper);
    }

    /**
     * 构建search查询条件 和老版本一致后续优化
     *
     * @param searchJson
     * @return
     */
    @Override
    public String buildSearchSql(final String searchJson) {
        StringBuilder searchSql = new StringBuilder();
        if (StringUtils.isNotEmpty(searchJson)) {
            List<WorkFlowTaskSearchFieldBean> fieldList = JSONArray.parseArray(searchJson, WorkFlowTaskSearchFieldBean.class);
            fieldList.forEach(each -> {
                //查询所有字段
                if ("all".equalsIgnoreCase(each.getKey()) && StringUtils.isNotEmpty(each.getValue())) {
                    searchSql.append(" and form_data like '%").append(each.getValue()).append("%'");

                } else if ("between".equalsIgnoreCase(each.getOperation())) {
                    //两个字段间查询
                    if ("date".equalsIgnoreCase(each.getFormat())) {
                        Date minDate = DateUtil.parse(each.getMinValue().toString());
                        Date maxDate = DateUtil.endOfDay(DateUtil.parse(each.getMaxValue().toString()));
                        searchSql.append(" and ((str_to_date(replace(JSON_EXTRACT(form_data, '$.").append(each.getKey()).append("'),'\\\"',''),'%Y-%m-%d %H:%i:%s')");
                        searchSql.append(" between ").append("'").append(minDate).append("'").append(" and ").append("'").append(maxDate).append("')");
                        searchSql.append(" or (str_to_date(replace(JSON_EXTRACT(form_data, '$.").append(each.getKey()).append("'),'\\\"',''),'%Y/%m/%d %H:%i:%s')");
                        searchSql.append(" between ").append("'").append(minDate).append("'").append(" and ").append("'").append(maxDate).append("'))");
                    }
                } else if (StringUtils.isNotEmpty(each.getValue())) {
                    searchSql.append(" and JSON_EXTRACT(form_data, '$.").append("\"").append(each.getKey()).append("\"").append("')").append(" like '%").append(each.getValue()).append("%' ");
                }
            });
            return searchSql.toString();
        }
        return null;
    }
}
